package icasue.reflect.handles.classes;

import icasue.reflect.handles.HandleSupplier;
import icasue.reflect.handles.OFAble;
import icasue.reflect.handles.exception.ExceptionOF;
import icasue.reflect.handles.object.ObjectOF;
import icasue.reflect.exceptions.HandleException;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.function.Function;
import java.util.function.Predicate;

/**
 * @Author: Qiao Hang
 * @CreateDate: 2020/12/1 上午10:31
 * @UpdateDate:
 * @Description:
 */
public class ClassOF implements OFAble {

    /**
     *  isAssignableFrom_ (pType,type)
     *  isInterface_ (type)
     *  isMemberClass_ (type)
     *  isAnnotation_ (type)
     *  isEnum_ (type)
     *  isArray_ (type)
     *  isInstance_ (type,obj)
     *  isAnonymousClass_ (type)
     *  isPrimitive_ (type)
     *
     *  getDeclaredMethod_  (type,mName,paramAry)
     *  getDeclaredMethods_ (type)
     *  getMethod_          (type,mName,paramAry)
     *  getMethods_         (type)
     *  getField_           (type,fName)
     *  getDeclaredField_   (type,fName)
     *  getFields_          (type)
     *  getDeclaredFields_  (type)
     *  getAnnotations_     (type)
     *  getAnnotationsByType_  (type,aType)
     *  getAnnotationsByType_  (type,aType)
     *  getDeclaredAnnotationsByType_  (type,aType)
     *  getEnumConstants_   (eType)
     *
     *  getClassLoader_   (type)
     *  getConstructor_   (type,paramArray)
     *  getConstructors_   (type)
     *  getDeclaredConstructor_   (type,paramArray)
     *  getDeclaredConstructors_   (type)
     *  getInterfaces_   (type)
     *  getSuperclass_   (type) ** null check
     *  getEnclosingClass_   (type) ** null check
     *  getPackage_   (type) ** null check
     *
     *  getName_    (type)
     *  getSimpleName_    (type)
     *  getCanonicalName_    (type)
     *  getModifiers_    (type)
     *  newInstance_    (type)
     *  changeParamArray21_    (cls,name,paramAry)
     *  changeParamArray11_    (name,paramAry)
     *
     *  All method check pass.
     */
    public static HandleSupplier.PredicateAry isAssignableFrom_ = (cls) -> {
        try {
            if (cls.length != 2)
                throw ExceptionOF.handleExcInvocation_.apply("ClassOF.isAssignableFrom_ : required 2 params.");
            return (boolean)ClassO.isAssignableFrom.bindTo(cls[0]).invoke(cls[1]);
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ClassOF.isAssignableFrom_ : occur an error : " , throwable.getMessage());
        }
    };

    public static Predicate<Object> isInterface_ = (cls) -> {
        try {
            return (boolean)ClassO.isInterface.bindTo(cls).invoke();
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ClassOF.isInterface_ : occur an error : " , throwable.getMessage());
        }
    };

    public static Predicate<Object> isMemberClass_ = (cls) -> {
        try {
            return (boolean)ClassO.isMemberClass.bindTo(cls).invoke();
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ClassOF.isMemberClass_ : occur an error : " , throwable.getMessage());
        }
    };

    public static Predicate<Object> isAnnotation_ = (cls) -> {
        try {
            return (boolean)ClassO.isAnnotation.bindTo(cls).invoke();
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ClassOF.isAnnotation_ : occur an error : " , throwable.getMessage());
        }
    };

    public static Predicate<Object> isEnum_ = (cls) -> {
        try {
            return (boolean)ClassO.isEnum.bindTo(cls).invoke();
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ClassOF.isEnum_ : occur an error : " , throwable.getMessage());
        }
    };

    public static Predicate<Object> isArray_ = (cls) -> {
        try {
            return (boolean)ClassO.isArray.bindTo(cls).invoke();
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ClassOF.isArray_ : occur an error : " , throwable.getMessage());
        }
    };

    public static HandleSupplier.PredicateAry isInstance_ = (clsAndIns) -> {
        try {
            if (clsAndIns.length != 2)
                throw ExceptionOF.handleExcInvocation_.apply("ClassOF.isInstance_ : required 2 params.");
            return (boolean)ClassO.isInstance.bindTo(clsAndIns[0]).invoke(clsAndIns[1]);
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ClassOF.isInstance_ : occur an error : " , throwable.getMessage());
        }
    };

    public static Predicate<Object> isAnonymousClass_ = (cls) -> {
        try {
            return (boolean)ClassO.isAnonymousClass.bindTo(cls).invoke();
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ClassOF.isAnonymousClass_ : occur an error : " , throwable.getMessage());
        }
    };

    public static Predicate<Object> isPrimitive_ = (cls) -> {
        try {
            return (boolean)ClassO.isPrimitive.invoke(cls);
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ClassOF.isPrimitive_ : occur an error : " , throwable.getMessage());
        }
    };


    /**
     *  不定源信息(可扩展)
     */
    public static HandleSupplier.FunctionAry<Method> getDeclaredMethod_ = (params) -> {
        try {
            if (params.length != 3)
                throw ExceptionOF.handleExcInvocation_.apply("ClassOF.getDeclaredMethod_ : required 3 params.");
            return (Method) ClassO.getDeclaredMethod.bindTo(params[0]).invoke(params[1],ClassOF.changeParamArray21_.apply(params));
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ClassOF.getMethod_ : occur an error : " , throwable.getMessage());
        }
    };

    public static Function<Object,Method[]> getDeclaredMethods_ = (cls) -> {
        try {
            return (Method[]) ClassO.getDeclaredMethods.bindTo(cls).invoke();
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ClassOF.getDeclaredMethods_ : occur an error : " , throwable.getMessage());
        }
    };

    public static HandleSupplier.FunctionAry<Method> getMethod_ = (params) -> {
        try {
            if (params.length != 3)
                throw ExceptionOF.handleExcInvocation_.apply("ClassOF.getMethod_ : required 3 params.");
            return (Method) ClassO.getMethod.bindTo(params[0]).invoke(params[1],ClassOF.changeParamArray21_.apply(params));
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ClassOF.getMethod_ : occur an error : " , throwable.getMessage());
        }
    };

    public static Function<Object,Method[]> getMethods_ = (cls) -> {
        try {
            return (Method[]) ClassO.getMethods.bindTo(cls).invoke();
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ClassOF.getMethods_ occur an error : " , throwable.getMessage());
        }
    };

    public static HandleSupplier.FunctionAry<Field> getField_ = (params) -> {
        try {
            if(params.length != 2)
                throw ExceptionOF.handleExcInvocation_.apply("ClassOF.getField_ : required 2 params.");
            return (Field) ClassO.getField.bindTo(params[0]).invoke(params[1]);
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ClassOF.getField_ : occur an error : " , throwable.getMessage());
        }
    };

    public static HandleSupplier.FunctionAry<Field> getDeclaredField_ = (params) -> {
        try {
            if(params.length != 2)
                throw ExceptionOF.handleExcInvocation_.apply("ClassOF.getDeclaredField_ : required 2 params.");
            return (Field) ClassO.getDeclaredField.bindTo(params[0]).invoke(params[1]);
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ClassOF.getDeclaredField_ : occur an error : " , throwable.getMessage());
        }
    };

    public static Function<Object, Field[]> getFields_ = (cls) -> {
        try {
            return (Field[]) ClassO.getFields.bindTo(cls).invoke();
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ClassOF.getFields_ : occur an error : " , throwable.getMessage());
        }
    };

    public static Function<Object,Field[]> getDeclaredFields_ = (cls) -> {
        try {
            return (Field[]) ClassO.getDeclaredFields.bindTo(cls).invoke();
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ClassOF.getDeclaredFields_ : occur an error : " , throwable.getMessage());
        }
    };

    public static Function<Object, Annotation[]> getAnnotations_ = (cls) -> {
        try {
            return (Annotation[]) ClassO.getAnnotations.bindTo(cls).invoke();
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ClassOF.getAnnotations_ : occur an error : " , throwable.getMessage());
        }
    };

    public static HandleSupplier.FunctionAry<Annotation[]> getAnnotationsByType_ = (params) -> {
        try {
            if(params.length != 2)
                throw ExceptionOF.handleExcInvocation_.apply("ClassOF.getAnnotationsByType_ required two params.");
            return (Annotation[]) ClassO.getAnnotationsByType.bindTo(params[0]).invoke(
                    ClassOF.cast_.apply(new Object[]{Class.class,params[1]})
            );
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ClassOF.getAnnotationsByType_ occur an error : " , throwable.getMessage());
        }
    };

    public static HandleSupplier.FunctionAry<Annotation[]> getDeclaredAnnotationsByType_ = (params) -> {
        try {
            if(params.length != 2)
                throw ExceptionOF.handleExcInvocation_.apply("ClassOF.getDeclaredAnnotationsByType_ required two params.");
            return (Annotation[]) ClassO.getDeclaredAnnotationsByType.bindTo(params[0]).invoke(
                    ClassOF.cast_.apply(new Object[]{Class.class,params[1]})
            );
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ClassOF.getDeclaredAnnotationsByType_ occur an error : " , throwable.getMessage());
        }
    };

    public static Function<Object,Object[]> getEnumConstants_ = (cls) -> {
        try {
            if(ClassOF.isEnum_.test(cls))
                return (Object[])ClassO.getEnumConstants.bindTo(cls).invoke();
            else
                throw ExceptionOF.handleExcInvocation_.apply("ClassOF.getEnumConstants_ : Class which you given can't AssignableFrom type enum.");
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ClassOF.getEnumConstants_ : occur an error : " , throwable.getMessage());
        }
    };


    /**
     * 相对原信息
     */
    public static Function<Object,ClassLoader> getClassLoader_ = (cls) -> {
        try {
            return (ClassLoader)ClassO.getClassLoader.bindTo(cls).invoke();
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ClassOF.getClassLoader_ occur an error : " , throwable.getMessage());
        }
    };

    public static HandleSupplier.FunctionAry<Constructor> getConstructor_ = (pars) -> {
        try {
            if(pars.length != 2)
                throw new HandleException("ClassOF.getConstructor_ : required 2 params.");
            return (Constructor)ClassO.getConstructor.bindTo(pars[0]).invoke(ClassOF.changeParamArray11_.apply(pars));
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ClassOF.getConstructor_ : Constructor not found.");
        }
    };

    public static HandleSupplier.FunctionAry<Constructor> getDeclaredConstructor_ = (pars) -> {
        try {
            if(pars.length != 2)
                throw new HandleException("ClassOF.getDeclaredConstructor_ : required 2 params.");
            return (Constructor)ClassO.getDeclaredConstructor.bindTo(pars[0]).invoke(ClassOF.changeParamArray11_.apply(pars));
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ClassOF.getDeclaredConstructor_ : Constructor not found.");
        }
    };

    public static Function<Object,Constructor[]> getConstructors_ = (cls) -> {
        try {
            return (Constructor[])ClassO.getConstructors.bindTo(cls).invoke();
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ClassOF.getConstructors_ : occur an error : " , throwable.getMessage());
        }
    };

    public static Function<Object,Constructor[]> getDeclaredConstructors_ = (cls) -> {
        try {
            return (Constructor[])ClassO.getDeclaredConstructors.bindTo(cls).invoke();
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ClassOF.getDeclaredConstructors_ : occur an error : " , throwable.getMessage());
        }
    };

    public static Function<Object,Class[]> getInterfaces_ = (cls) -> {
        try {
            return (Class[])ClassO.getInterfaces.bindTo(cls).invoke();
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ClassOF.getInterfaces_ : occur an error : " , throwable.getMessage());
        }
    };

    public static Function<Object,Class> getSuperclass_ = (cls) -> {
        try {
            return (Class)ClassO.getSuperclass.bindTo(cls).invoke();
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ClassOF.getSuperclass_ : occur an error : " , throwable.getMessage());
        }
    };

    public static Function<Object,Class> getEnclosingClass_ = (cls) -> {
        try {
            return (Class)ClassO.getEnclosingClass.bindTo(cls).invoke();
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ClassOF.getEnclosingClass_ : occur an error : " , throwable.getMessage());
        }
    };

    public static Function<Object,Package> getPackage_ = (cls) -> {
        try {
            return (Package)ClassO.getPackage.bindTo(cls).invoke();
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ClassOF.getPackage_ : occur an error : " , throwable.getMessage());
        }
    };

    /**
     *  根可读源信息
     */
    public static Function<Object,String> getName_ = (cls) -> {
        try {
            return (String)ClassO.getName.bindTo(cls).invoke();
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ClassOF.getName_ : occur an error : " , throwable.getMessage());
        }
    };

    public static Function<Object,String> getSimpleName_ = (cls) -> {
        try {
            return (String)ClassO.getSimpleName.bindTo(cls).invoke();
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ClassOF.getSimpleName_ : occur an error : " , throwable.getMessage());
        }
    };

    public static Function<Object,String> getCanonicalName_ = (cls) -> {
        try {
            return (String)ClassO.getCanonicalName.bindTo(cls).invoke();
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ClassOF.getCanonicalName_ : occur an error : " , throwable.getMessage());
        }
    };

    public static Function<Object,Integer> getModifiers_ = (cls) -> {
        try {
            return (int) ClassO.getModifiers.bindTo(cls).invoke();
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ClassOF.getModifiers_ : occur an error : " , throwable.getMessage());
        }
    };

    /**
     *  深度源信息
     */
    public static Function<Object,Object> newInstance_ = (cls) -> {
        try {
            return ClassO.newInstance.bindTo(cls).invoke();
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ClassOF.newInstance_ : occur an error : " , throwable.getMessage());
        }
    };

    public static HandleSupplier.FunctionAry<Object> cast_ = (params) -> {
        try {
            if(params.length != 2)
                throw ExceptionOF.handleExcInvocation_.apply("ClassOF.cast_ : required 2 params.");
            return ClassO.cast.bindTo(params[0]).invoke(params[1]);
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ClassOF.cast_ : occur an error : " , throwable.getMessage());
        }
    };

    /**
     * 参数转换
     */
    public static HandleSupplier.FunctionAry<Class[]> changeParamArray21_ = (clsNameParamArray) -> {
        try {
            if(ObjectOF.isNull_.test(clsNameParamArray[2]) || Object[].class.cast(clsNameParamArray[2]).length == 0)
                return new Class[]{};
            if(ObjectOF.equals_.test(new Object[]{Class[].class,clsNameParamArray[2].getClass()}))
                return (Class[])clsNameParamArray[2];
            if(!ObjectOF.notEmptyAry_.test(clsNameParamArray[2]))
                throw ExceptionOF.handleExcInvocation_.apply(
                        "ClassOF.changeParamArray21_ : can't find param's class for null value which given in paramValueArray." +
                                "if your paramValue mayBe include null, we support two reflect.api for using, " +
                                "first : MethodOF.findMethodAndInvoker_type_mName_paramClassArray  " +
                                "second : MethodOF.invoke_method_invoker_paramArray_");
            Class[] paramClsAry = Arrays.stream(clsNameParamArray)
                    .skip(2).limit(1)
                    .map(param -> Object[].class.cast(param))
                    .flatMap(param -> Arrays.stream(param))
                    .map(param -> param instanceof Class ? param : param.getClass())
                    .toArray(Class[]::new);
            return paramClsAry;
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ClassOF.changeParamArray21_ execute occur an error : " , throwable.getMessage());
        }
    };

    public static HandleSupplier.FunctionAry<Class[]> changeParamArray11_ = (clsNameParamArray) -> {
        try {
            if(ObjectOF.isNull_.test(clsNameParamArray[1]) || Object[].class.cast(clsNameParamArray[1]).length == 0)
                return new Class[]{};
            if(ObjectOF.equals_.test(new Object[]{Class[].class,clsNameParamArray[1].getClass()}))
                return (Class[])clsNameParamArray[1];
            if(!ObjectOF.notEmptyAry_.test(clsNameParamArray[1]))
                throw ExceptionOF.handleExcInvocation_.apply( "ClassOF.changeParamArray11_ : can't find param's class for null value which given in paramValueArray.");
            Class[] paramClsAry = Arrays.stream(clsNameParamArray)
                    .skip(1).limit(1)
                    .map(param -> Object[].class.cast(param))
                    .flatMap(param -> Arrays.stream(param))
                    .map(param -> param instanceof Class ? param : param.getClass())
                    .toArray(Class[]::new);
            return paramClsAry;
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ClassOF.changeParam21_ execute occur an error : " , throwable.getMessage());
        }
    };

}
